﻿package  systems.traer
{
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageDisplayState;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Vector3D;
	import shapes.delaunay.Delaunay;
	import shapes.delaunay.DelaunayTriangle;
	import shapes.shapes2D.polygon.ConvexHull;
	import shapes.shapes2D.polygon.Triangulation;
	
	/**
	 * @author Nicolas Barradeau
	 */
	public class Arboreum extends Sprite
	{
		protected var NODE_SIZE:Number = 1;
		protected var EDGE_LENGTH:Number = 10;
		protected var EDGE_STRENGTH:Number = 0.2;
		protected var SPACER_STRENGTH:Number = 10000;

		//ParticleSystem physics;
		protected var  scale:Number = 1;
		protected var  centroidX:Number = 0;
		protected var  centroidY:Number = 0;
		
		public function Arboreum() 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		
		protected var canvas:Shape = new Shape();
		protected var physics:ParticleSystem;
		protected var particles:Vector.<Particle>;
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.displayState = StageDisplayState.FULL_SCREEN;
			
			physics = new ParticleSystem( new Vector3D( 0.00000000001, 0, 0 ), 0.99 );
			particles = new Vector.<Particle>();
			
			addChild( canvas );
			
			// Now try this to see make it more damped, but stable.
			//physics.drag = 0.001;
			
			initialize();
			
			addEventListener( Event.ENTER_FRAME, oef );
			stage.addEventListener( MouseEvent.MOUSE_DOWN, mHandler );
		}
		
		private function mHandler( e:Event ):void
		{
			addNode();
		}
		
		private function oef( e:Event ):void
		{
			physics.tick(); 
			
			if ( physics.numberOfParticles() > 1 ) 
			{
				updateCentroid();
			
				x = stage.stageWidth / 2 - centroidX;
				y = stage.stageHeight / 2 - centroidY;
			}
			
			scaleX = scaleY = scale;
			
			draw();  
		}
		
		
		private function addSpacersToNode( p:Particle, r:Particle ):void
		{
			var i:int;
			var q:Particle;
			for ( i = 0; i < physics.numberOfParticles(); ++i )
			{
				q = physics.getParticleById( i );
				
				if ( p != q && p != r )	physics.makeAttraction( p, q, -SPACER_STRENGTH, 20 );
				
			}
		}

		private function makeEdgeBetween( a:Particle, b:Particle ):void
		{
			physics.makeSpring( a, b, EDGE_STRENGTH, EDGE_STRENGTH, EDGE_LENGTH );
		}

		private function initialize():void
		{
			
			physics.clear();
			physics.makeParticle();
			
		}

		private function addNode():void
		{ 
			
			var q:Particle = physics.getParticleById( int( Math.random() * physics.numberOfParticles() ) );
			
			var p:Particle = physics.makeParticle();
			
			p.position.x = q.position.x + Math.random() - .5;// ? -1:1;
			p.position.y = q.position.y + Math.random() - .5;// ? -1:1; 
			p.position.z = q.position.z + Math.random() - .5;// ? -1:1;
			
			addSpacersToNode( p, q );
			
			makeEdgeBetween( p, q );
			
			
		}
		
		
		private function updateCentroid():void
		{
		
			var xMax:Number = Number.NEGATIVE_INFINITY;
			var xMin:Number = Number.POSITIVE_INFINITY;
			var yMax:Number = Number.NEGATIVE_INFINITY; 
			var yMin:Number = Number.POSITIVE_INFINITY;
			
			var i:int;
			var p:Particle;
			for ( i = 0; i < physics.numberOfParticles(); ++i )
			{
				p = physics.getParticleById( i );
				
				if ( xMin > p.position.x ) xMin = p.position.x;
				if ( xMax < p.position.x ) xMax = p.position.x;
				if ( yMin > p.position.y ) yMin = p.position.y;
				if ( yMax < p.position.y ) yMax = p.position.y;
				
			}
			
			var deltaX:Number = xMax-xMin;
			var deltaY:Number = yMax-yMin;

			centroidX = xMin + 0.5 * deltaX;
			centroidY = yMin + 0.5 * deltaY;
			
			if ( deltaY > deltaX )
			{
				scale = canvas.height/deltaY;
			}
			else 
			{
				scale = canvas.width/deltaX;
			}
			
		}
		
		private function draw():void
		{      
			canvas.graphics.clear();
			canvas.graphics.lineStyle( 2, 0xFFFFFF );
			var v:Particle;
			var s:Spring;
			var points:Vector.<Point> = new Vector.<Point>();
			var i:int;
			// draw vertices
			for ( i = 0; i < physics.numberOfParticles(); ++i )
			{
				v = physics.getParticleById( i );
				v.draw( canvas.graphics, 5 );
				points.push( new Point( v.position.x, v.position.y ) );
			}

			// draw edges 
			//for ( i = 0; i < physics.numberOfSprings(); ++i )
			//{
				//s = physics.getSpringById( i );
				//s.draw( canvas.graphics );
			//}
			
			canvas.graphics.lineStyle( 4, 0xFFFF00 );
			plotLines( ConvexHull.process( points ) );
			
			
			canvas.graphics.lineStyle( 2, 0xFF0000 );
			points = Triangulation.process( points );
			var delaunay:Vector.<DelaunayTriangle> = Delaunay.Triangulate( points );
			if ( delaunay != null ) plotTriss( delaunay );
			
			
			
		}
		
		private function plotTriss(delaunay:Vector.<DelaunayTriangle> = null ):void 
		{
			
			for each( var t:DelaunayTriangle in delaunay )
			{
				t.draw( canvas.graphics );
			}
		}
		
		
		private function plotLines( points:Vector.<Point>):void
		{
			if ( points.length == 0 ) return;
			var p:Point = points[ 0 ];
			canvas.graphics.moveTo( p.x, p.y );
			
			for each( p in points )
			{
				
				canvas.graphics.lineTo( p.x, p.y );
				
			}
			canvas.graphics.lineTo( points[ 0 ].x, points[ 0 ].y );
			
		}
	}
}